#ifndef _MATRIX4_HPP_
#define _MATRIX4_HPP_

typedef struct { float data[12]; } Float12;

typedef struct { float data[16]; } Float16;

typedef struct { float data[8]; } Float8;

typedef struct { float data[4]; } Float4;

const float idetityMatrixData[16] = {
	1.0f, 0.0f, 0.0f, 0.0f,
	0.0f, 1.0f, 0.0f, 0.0f,
	0.0f, 0.0f, 1.0f, 0.0f,
	0.0f, 0.0f, 0.0f, 1.0f
};

class Matrix4 {
private:

	float data[16];
	
public:

	float* getPtr() {
		return (float*)this;
	}

	const float* getPtr() const {
		return (float*)this;
	}
	
	float* operator [] (unsigned int index) {
		return data + (index * 4);
	}

	bool operator == (const Matrix4& m) const {
		for (unsigned int i = 0; i < 16; i++) {
			if (data[i] != m.data[i]) {
				return false;
			}
		}
		return true;
	}

	bool operator != (const Matrix4& m) const {
		for (unsigned int i = 0; i < 16; i++) {
			if (data[i] != m.data[i]) {
				return true;
			}
		}
		return true;
	}

	Matrix4 operator + (const Matrix4& m) const {
		return Matrix4(
			data[ 0] + m.data[ 0],
			data[ 1] + m.data[ 1],
			data[ 2] + m.data[ 2],
			data[ 3] + m.data[ 3],
			data[ 4] + m.data[ 4],
			data[ 5] + m.data[ 5],
			data[ 6] + m.data[ 6],
			data[ 7] + m.data[ 7],
			data[ 8] + m.data[ 8],
			data[ 9] + m.data[ 9],
			data[10] + m.data[10],
			data[11] + m.data[11],
			data[12] + m.data[12],
			data[13] + m.data[13],
			data[14] + m.data[14],
			data[15] + m.data[15]
		);
	}
	
	Matrix4 operator - (const Matrix4& m) const {
		return Matrix4(
			data[ 0] - m.data[ 0],
			data[ 1] - m.data[ 1],
			data[ 2] - m.data[ 2],
			data[ 3] - m.data[ 3],
			data[ 4] - m.data[ 4],
			data[ 5] - m.data[ 5],
			data[ 6] - m.data[ 6],
			data[ 7] - m.data[ 7],
			data[ 8] - m.data[ 8],
			data[ 9] - m.data[ 9],
			data[10] - m.data[10],
			data[11] - m.data[11],
			data[12] - m.data[12],
			data[13] - m.data[13],
			data[14] - m.data[14],
			data[15] - m.data[15]
		);
	}
	
	Matrix4 operator * (const Matrix4& m) const {
		return Matrix4(
			data[ 0] * m.data[ 0] + data[ 4] * m.data[ 1] + data[ 8] * m.data[ 2] + data[12] * m.data[ 3],
			data[ 1] * m.data[ 0] + data[ 5] * m.data[ 1] + data[ 9] * m.data[ 2] + data[13] * m.data[ 3],
			data[ 2] * m.data[ 0] + data[ 6] * m.data[ 1] + data[10] * m.data[ 2] + data[14] * m.data[ 3],
			data[ 3] * m.data[ 0] + data[ 7] * m.data[ 1] + data[11] * m.data[ 2] + data[15] * m.data[ 3],
			data[ 0] * m.data[ 4] + data[ 4] * m.data[ 5] + data[ 8] * m.data[ 6] + data[12] * m.data[ 7],
			data[ 1] * m.data[ 4] + data[ 5] * m.data[ 5] + data[ 9] * m.data[ 6] + data[13] * m.data[ 7],
			data[ 2] * m.data[ 4] + data[ 6] * m.data[ 5] + data[10] * m.data[ 6] + data[14] * m.data[ 7],
			data[ 3] * m.data[ 4] + data[ 7] * m.data[ 5] + data[11] * m.data[ 6] + data[15] * m.data[ 7],
			data[ 0] * m.data[ 8] + data[ 4] * m.data[ 9] + data[ 8] * m.data[10] + data[12] * m.data[11],
			data[ 1] * m.data[ 8] + data[ 5] * m.data[ 9] + data[ 9] * m.data[10] + data[13] * m.data[11],
			data[ 2] * m.data[ 8] + data[ 6] * m.data[ 9] + data[10] * m.data[10] + data[14] * m.data[11],
			data[ 3] * m.data[ 8] + data[ 7] * m.data[ 9] + data[11] * m.data[10] + data[15] * m.data[11],
			data[ 0] * m.data[12] + data[ 4] * m.data[13] + data[ 8] * m.data[14] + data[12] * m.data[15],
			data[ 1] * m.data[12] + data[ 5] * m.data[13] + data[ 9] * m.data[14] + data[13] * m.data[15],
			data[ 2] * m.data[12] + data[ 6] * m.data[13] + data[10] * m.data[14] + data[14] * m.data[15],
			data[ 3] * m.data[12] + data[ 7] * m.data[13] + data[11] * m.data[14] + data[15] * m.data[15]
		);
	};
	
	Matrix4 operator / (const Matrix4& m) const {
		return *this * inverse(m);
	}
	
	void operator += (const Matrix4& m) {
		data[ 0] += m.data[ 0];
		data[ 1] += m.data[ 1];
		data[ 2] += m.data[ 2];
		data[ 3] += m.data[ 3];
		data[ 4] += m.data[ 4];
		data[ 5] += m.data[ 5];
		data[ 6] += m.data[ 6];
		data[ 7] += m.data[ 7];
		data[ 8] += m.data[ 8];
		data[ 9] += m.data[ 9];
		data[10] += m.data[10];
		data[11] += m.data[11];
		data[12] += m.data[12];
		data[13] += m.data[13];
		data[14] += m.data[14];
		data[15] += m.data[15];
	}
	
	void operator -= (const Matrix4& m) {
		data[ 0] -= m.data[ 0];
		data[ 1] -= m.data[ 1];
		data[ 2] -= m.data[ 2];
		data[ 3] -= m.data[ 3];
		data[ 4] -= m.data[ 4];
		data[ 5] -= m.data[ 5];
		data[ 6] -= m.data[ 6];
		data[ 7] -= m.data[ 7];
		data[ 8] -= m.data[ 8];
		data[ 9] -= m.data[ 9];
		data[10] -= m.data[10];
		data[11] -= m.data[11];
		data[12] -= m.data[12];
		data[13] -= m.data[13];
		data[14] -= m.data[14];
		data[15] -= m.data[15];
	}
	
	void operator *= (const Matrix4& m) {
		*this = *this * m;
	}
	
	void operator /= (const Matrix4& m) {
		*this = *this * inverse(m);
	}
	
	Matrix4 operator + (float s) const {
		return Matrix4(
			data[ 0] + s,
			data[ 1] + s,
			data[ 2] + s,
			data[ 3] + s,
			data[ 4] + s,
			data[ 5] + s,
			data[ 6] + s,
			data[ 7] + s,
			data[ 8] + s,
			data[ 9] + s,
			data[10] + s,
			data[11] + s,
			data[12] + s,
			data[13] + s,
			data[14] + s,
			data[15] + s
		);
	}
	
	Matrix4 operator - (float s) const {
		return Matrix4(
			data[ 0] - s,
			data[ 1] - s,
			data[ 2] - s,
			data[ 3] - s,
			data[ 4] - s,
			data[ 5] - s,
			data[ 6] - s,
			data[ 7] - s,
			data[ 8] - s,
			data[ 9] - s,
			data[10] - s,
			data[11] - s,
			data[12] - s,
			data[13] - s,
			data[14] - s,
			data[15] - s
		);
	}

	Matrix4 operator * (float s) const {
		return Matrix4(
			data[ 0] * s,
			data[ 1] * s,
			data[ 2] * s,
			data[ 3] * s,
			data[ 4] * s,
			data[ 5] * s,
			data[ 6] * s,
			data[ 7] * s,
			data[ 8] * s,
			data[ 9] * s,
			data[10] * s,
			data[11] * s,
			data[12] * s,
			data[13] * s,
			data[14] * s,
			data[15] * s
		);
	}
	
	Matrix4 operator / (float s) const {
		return Matrix4(
			data[ 0] / s,
			data[ 1] / s,
			data[ 2] / s,
			data[ 3] / s,
			data[ 4] / s,
			data[ 5] / s,
			data[ 6] / s,
			data[ 7] / s,
			data[ 8] / s,
			data[ 9] / s,
			data[10] / s,
			data[11] / s,
			data[12] / s,
			data[13] / s,
			data[14] / s,
			data[15] / s
		);
	}
	
	void operator += (float s) {
		data[ 0] += s;
		data[ 1] += s;
		data[ 2] += s;
		data[ 3] += s;
		data[ 4] += s;
		data[ 5] += s;
		data[ 6] += s;
		data[ 7] += s;
		data[ 8] += s;
		data[ 9] += s;
		data[10] += s;
		data[11] += s;
		data[12] += s;
		data[13] += s;
		data[14] += s;
		data[15] += s;
	}
	
	void operator -= (float s) {
		data[ 0] -= s;
		data[ 1] -= s;
		data[ 2] -= s;
		data[ 3] -= s;
		data[ 4] -= s;
		data[ 5] -= s;
		data[ 6] -= s;
		data[ 7] -= s;
		data[ 8] -= s;
		data[ 9] -= s;
		data[10] -= s;
		data[11] -= s;
		data[12] -= s;
		data[13] -= s;
		data[14] -= s;
		data[15] -= s;
	}
	
	void operator *= (float s) {
		data[ 0] *= s;
		data[ 1] *= s;
		data[ 2] *= s;
		data[ 3] *= s;
		data[ 4] *= s;
		data[ 5] *= s;
		data[ 6] *= s;
		data[ 7] *= s;
		data[ 8] *= s;
		data[ 9] *= s;
		data[10] *= s;
		data[11] *= s;
		data[12] *= s;
		data[13] *= s;
		data[14] *= s;
		data[15] *= s;
	}

	void operator /= (float s) {
		data[ 0] /= s;
		data[ 1] /= s;
		data[ 2] /= s;
		data[ 3] /= s;
		data[ 4] /= s;
		data[ 5] /= s;
		data[ 6] /= s;
		data[ 7] /= s;
		data[ 8] /= s;
		data[ 9] /= s;
		data[10] /= s;
		data[11] /= s;
		data[12] /= s;
		data[13] /= s;
		data[14] /= s;
		data[15] /= s;
	}
	
public:

	/*
		Транспонирование матрицы (отражение вдоль главной диагонали)
	*/

	Matrix4 transpose() const {
		return Matrix4(
			data[ 0],
			data[ 4],
			data[ 8],
			data[ 12],
			data[ 1],
			data[ 5],
			data[ 9],
			data[ 13],
			data[ 2],
			data[ 6],
			data[ 10],
			data[ 14],
			data[ 3],
			data[ 7],
			data[ 11],
			data[ 15]
		);
	}

	/*
		Транспонирование матрицы (отражение вдоль главной диагонали)
	*/

	static Matrix4 transpose(const Matrix4& m) {
		return Matrix4(
			m.data[ 0],
			m.data[ 4],
			m.data[ 8],
			m.data[ 12],
			m.data[ 1],
			m.data[ 5],
			m.data[ 9],
			m.data[ 13],
			m.data[ 2],
			m.data[ 6],
			m.data[ 10],
			m.data[ 14],
			m.data[ 3],
			m.data[ 7],
			m.data[ 11],
			m.data[ 15]
		);
	}

	/*
		Инверсия матрицы
	*/

	static Matrix4 inverse(const Matrix4& m) {
		return Matrix4();
	}
	
	/*
		Поворот матрицы вокруг свободного вектора
	*/
	
	void rotate(float angle, const Vector3& vector) {
		Vector3 v = Vector3::normalize(vector);
		
		float c = cos(angle);
		float s = sin(angle);
		float c1 = 1.0f - c;
		
		float xs = v.x * s;
		float ys = v.y * s;
		float zs = v.z * s;
		
		float xy = v.x * v.y * c1;
		float xz = v.x * v.z * c1;
		float yz = v.y * v.z * c1;
		
		float rot0 = v.x * v.x * c1 + c;
		float rot1 = xy + zs;
		float rot2 = xz - ys;
		float rot4 = xy - zs;
		float rot5 = v.y * v.y * c1 + c;
		float rot6 = yz + xs;
		float rot8 = xz + ys;
		float rot9 = yz - xs;
		float rot10 = v.z * v.z * c1 + c;
		
		float result[12];
		
		result[ 0] = data[ 0] * rot0 + data[ 4] * rot1 + data[ 8] * rot2;
		result[ 1] = data[ 1] * rot0 + data[ 5] * rot1 + data[ 9] * rot2;
		result[ 2] = data[ 2] * rot0 + data[ 6] * rot1 + data[10] * rot2;
		result[ 3] = data[ 3] * rot0 + data[ 7] * rot1 + data[11] * rot2;
		
		result[ 4] = data[ 0] * rot4 + data[ 4] * rot5 + data[ 8] * rot6;
		result[ 5] = data[ 1] * rot4 + data[ 5] * rot5 + data[ 9] * rot6;
		result[ 6] = data[ 2] * rot4 + data[ 6] * rot5 + data[10] * rot6;
		result[ 7] = data[ 3] * rot4 + data[ 7] * rot5 + data[11] * rot6;
		
		result[ 8] = data[ 0] * rot8 + data[ 4] * rot9 + data[ 8] * rot10;
		result[ 9] = data[ 1] * rot8 + data[ 5] * rot9 + data[ 9] * rot10;
		result[10] = data[ 2] * rot8 + data[ 6] * rot9 + data[10] * rot10;
		result[11] = data[ 3] * rot8 + data[ 7] * rot9 + data[11] * rot10;
		
		*(Float12*)(data) = *(Float12*)(result);
	}
	
	/*
		Поворот матрицы вокруг свободного вектора
	*/
	
	void rotate(float angle, float x, float y, float z) {
		Vector3 v = Vector3::normalize(Vector3(x, y, z));
		
		float c = cos(angle);
		float s = sin(angle);
		float c1 = 1.0f - c;
		
		float xs = v.x * s;
		float ys = v.y * s;
		float zs = v.z * s;
		
		float xy = v.x * v.y * c1;
		float xz = v.x * v.z * c1;
		float yz = v.y * v.z * c1;
		
		float rot0 = v.x * v.x * c1 + c;
		float rot1 = xy + zs;
		float rot2 = xz - ys;
		float rot4 = xy - zs;
		float rot5 = v.y * v.y * c1 + c;
		float rot6 = yz + xs;
		float rot8 = xz + ys;
		float rot9 = yz - xs;
		float rot10 = v.z * v.z * c1 + c;
		
		float result[12];
		
		result[ 0] = data[ 0] * rot0 + data[ 4] * rot1 + data[ 8] * rot2;
		result[ 1] = data[ 1] * rot0 + data[ 5] * rot1 + data[ 9] * rot2;
		result[ 2] = data[ 2] * rot0 + data[ 6] * rot1 + data[10] * rot2;
		result[ 3] = data[ 3] * rot0 + data[ 7] * rot1 + data[11] * rot2;
		
		result[ 4] = data[ 0] * rot4 + data[ 4] * rot5 + data[ 8] * rot6;
		result[ 5] = data[ 1] * rot4 + data[ 5] * rot5 + data[ 9] * rot6;
		result[ 6] = data[ 2] * rot4 + data[ 6] * rot5 + data[10] * rot6;
		result[ 7] = data[ 3] * rot4 + data[ 7] * rot5 + data[11] * rot6;
		
		result[ 8] = data[ 0] * rot8 + data[ 4] * rot9 + data[ 8] * rot10;
		result[ 9] = data[ 1] * rot8 + data[ 5] * rot9 + data[ 9] * rot10;
		result[10] = data[ 2] * rot8 + data[ 6] * rot9 + data[10] * rot10;
		result[11] = data[ 3] * rot8 + data[ 7] * rot9 + data[11] * rot10;
		
		*(Float12*)(data) = *(Float12*)(result);
	}

	/*
		Поворот вокруг оси X
	*/
	
	void rotateX(float angle) {
		float c = cos(angle);
		float s = sin(angle);
		float result[8];
		result[ 0] = data[ 4] * c - data[ 8] * s;
		result[ 1] = data[ 5] * c - data[ 9] * s;
		result[ 2] = data[ 6] * c - data[10] * s;
		result[ 3] = data[ 7] * c - data[11] * s;
		result[ 4] = data[ 4] * s + data[ 8] * c;
		result[ 5] = data[ 5] * s + data[ 9] * c;
		result[ 6] = data[ 6] * s + data[10] * c;
		result[ 7] = data[ 7] * s + data[11] * c;
		*((Float8*)(data + 4)) = *((Float8*)(result));
	}
	
	/*
		Поворот вокруг оси X
	*/
	
	static Matrix4 rotateX(const Matrix4& m, float angle) {
		float c = cos(angle);
		float s = sin(angle);
		return Matrix4(
			m.data[ 0],
			m.data[ 1],
			m.data[ 2],
			m.data[ 3],
			m.data[ 4] * c - m.data[ 8] * s,
			m.data[ 5] * c - m.data[ 9] * s,
			m.data[ 6] * c - m.data[10] * s,
			m.data[ 7] * c - m.data[11] * s,
			m.data[ 4] * s + m.data[ 8] * c,
			m.data[ 5] * s + m.data[ 9] * c,
			m.data[ 6] * s + m.data[10] * c,
			m.data[ 7] * s + m.data[11] * c,
			m.data[12],
			m.data[13],
			m.data[14],
			m.data[15]
		);
	}
	
	/*
		Поворот вокруг оси Y
	*/

	void rotateY(float angle) {
		float c = cos(angle);
		float s = sin(angle);
		float result[8];
		result[ 0] = data[ 0] * c + data[ 8] * s;
		result[ 1] = data[ 1] * c + data[ 9] * s;
		result[ 2] = data[ 2] * c + data[10] * s;
		result[ 3] = data[ 3] * c + data[11] * s;
		result[ 4] = data[ 8] * c - data[ 0] * s;
		result[ 5] = data[ 9] * c - data[ 1] * s;
		result[ 6] = data[10] * c - data[ 2] * s;
		result[ 7] = data[11] * c - data[ 3] * s;
		*((Float4*)(data)) = *((Float4*)(result));
		*((Float4*)(data + 8)) = *((Float4*)(result + 4));
	}
	
	/*
		Поворот вокруг оси Y
	*/

	static Matrix4 rotateY(const Matrix4& m, float angle) {
		float c = cos(angle);
		float s = sin(angle);
		return Matrix4(
			m.data[ 0] * c + m.data[ 8] * s,
			m.data[ 1] * c + m.data[ 9] * s,
			m.data[ 2] * c + m.data[10] * s,
			m.data[ 3] * c + m.data[11] * s,
			m.data[ 4],
			m.data[ 5],
			m.data[ 6],
			m.data[ 7],
			m.data[ 8] * c - m.data[ 0] * s,
			m.data[ 9] * c - m.data[ 1] * s,
			m.data[10] * c - m.data[ 2] * s,
			m.data[11] * c - m.data[ 3] * s,
			m.data[12],
			m.data[13],
			m.data[14],
			m.data[15]
		);
	}

	/*
		Поворот вокруг оси Z
	*/
	
	static Matrix4 rotateZ(const Matrix4& m, float angle) {
		float c = cos(angle);
		float s = sin(angle);
		return Matrix4(
			m.data[ 0] * c - m.data[ 4] * s,
			m.data[ 1] * c - m.data[ 5] * s,
			m.data[ 2] * c - m.data[ 6] * s,
			m.data[ 3] * c - m.data[ 7] * s,
			m.data[ 0] * s + m.data[ 4] * c,
			m.data[ 1] * s + m.data[ 5] * c,
			m.data[ 2] * s + m.data[ 6] * c,
			m.data[ 3] * s + m.data[ 7] * c,
			m.data[ 8],
			m.data[ 9],
			m.data[10],
			m.data[11],
			m.data[12],
			m.data[13],
			m.data[14],
			m.data[15]
		);
	}
	
	/*
		Поворот вокруг оси Z
	*/
	
	void rotateZ(float angle) {
		float c = cos(angle);
		float s = sin(angle);
		float result[8];
		result[ 0] = data[ 0] * c - data[ 4] * s;
		result[ 1] = data[ 1] * c - data[ 5] * s;
		result[ 2] = data[ 2] * c - data[ 6] * s;
		result[ 3] = data[ 3] * c - data[ 7] * s;
		result[ 4] = data[ 0] * s + data[ 4] * c;
		result[ 5] = data[ 1] * s + data[ 5] * c;
		result[ 6] = data[ 2] * s + data[ 6] * c;
		result[ 7] = data[ 3] * s + data[ 7] * c;
		*((Float8*)(data)) = *((Float8*)(result));
	}

	/*
		Поворот матрицы вокруг свободного вектора
	*/
	
	static Matrix4 rotate(const Matrix4& m, float angle, const Vector3& vector) {
		Vector3 v = Vector3::normalize(vector);
		
		float c = cos(angle);
		float s = sin(angle);
		float c1 = 1.0f - c;
		
		float xs = v.x * s;
		float ys = v.y * s;
		float zs = v.z * s;
		
		float xy = v.x * v.y * c1;
		float xz = v.x * v.z * c1;
		float yz = v.y * v.z * c1;
		
		float rot0 = v.x * v.x * c1 + c;
		float rot1 = xy + zs;
		float rot2 = xz - ys;
		float rot4 = xy - zs;
		float rot5 = v.y * v.y * c1 + c;
		float rot6 = yz + xs;
		float rot8 = xz + ys;
		float rot9 = yz - xs;
		float rot10 = v.z * v.z * c1 + c;
		
		return Matrix4(
			m.data[ 0] * rot0 + m.data[ 4] * rot1 + m.data[ 8] * rot2,
			m.data[ 1] * rot0 + m.data[ 5] * rot1 + m.data[ 9] * rot2,
			m.data[ 2] * rot0 + m.data[ 6] * rot1 + m.data[10] * rot2,
			m.data[ 3] * rot0 + m.data[ 7] * rot1 + m.data[11] * rot2,
			
			m.data[ 0] * rot4 + m.data[ 4] * rot5 + m.data[ 8] * rot6,
			m.data[ 1] * rot4 + m.data[ 5] * rot5 + m.data[ 9] * rot6,
			m.data[ 2] * rot4 + m.data[ 6] * rot5 + m.data[10] * rot6,
			m.data[ 3] * rot4 + m.data[ 7] * rot5 + m.data[11] * rot6,
			
			m.data[ 0] * rot8 + m.data[ 4] * rot9 + m.data[ 8] * rot10,
			m.data[ 1] * rot8 + m.data[ 5] * rot9 + m.data[ 9] * rot10,
			m.data[ 2] * rot8 + m.data[ 6] * rot9 + m.data[10] * rot10,
			m.data[ 3] * rot8 + m.data[ 7] * rot9 + m.data[11] * rot10,
			
			m.data[12],
			m.data[13],
			m.data[14],
			m.data[15]
		);
	}
	
	/*
		Поворот матрицы вокруг свободного вектора
	*/
	
	static Matrix4 rotate(const Matrix4& m, float angle, float x, float y, float z) {
		Vector3 v = Vector3::normalize(Vector3(x, y, z));
		
		float c = cos(angle);
		float s = sin(angle);
		float c1 = 1.0f - c;
		
		float xs = v.x * s;
		float ys = v.y * s;
		float zs = v.z * s;
		
		float xy = v.x * v.y * c1;
		float xz = v.x * v.z * c1;
		float yz = v.y * v.z * c1;
		
		float rot0 = v.x * v.x * c1 + c;
		float rot1 = xy + zs;
		float rot2 = xz - ys;
		float rot4 = xy - zs;
		float rot5 = v.y * v.y * c1 + c;
		float rot6 = yz + xs;
		float rot8 = xz + ys;
		float rot9 = yz - xs;
		float rot10 = v.z * v.z * c1 + c;
		
		return Matrix4(
			m.data[ 0] * rot0 + m.data[ 4] * rot1 + m.data[ 8] * rot2,
			m.data[ 1] * rot0 + m.data[ 5] * rot1 + m.data[ 9] * rot2,
			m.data[ 2] * rot0 + m.data[ 6] * rot1 + m.data[10] * rot2,
			m.data[ 3] * rot0 + m.data[ 7] * rot1 + m.data[11] * rot2,
			
			m.data[ 0] * rot4 + m.data[ 4] * rot5 + m.data[ 8] * rot6,
			m.data[ 1] * rot4 + m.data[ 5] * rot5 + m.data[ 9] * rot6,
			m.data[ 2] * rot4 + m.data[ 6] * rot5 + m.data[10] * rot6,
			m.data[ 3] * rot4 + m.data[ 7] * rot5 + m.data[11] * rot6,
			
			m.data[ 0] * rot8 + m.data[ 4] * rot9 + m.data[ 8] * rot10,
			m.data[ 1] * rot8 + m.data[ 5] * rot9 + m.data[ 9] * rot10,
			m.data[ 2] * rot8 + m.data[ 6] * rot9 + m.data[10] * rot10,
			m.data[ 3] * rot8 + m.data[ 7] * rot9 + m.data[11] * rot10,
			
			m.data[12],
			m.data[13],
			m.data[14],
			m.data[15]
		);
	}

	/*
		Поворот матрицы на углы Эйлера
	*/
	
	void rotateEuler(float pitch, float yaw, float roll) {
		*this *= EulerToMatrix4(yaw, pitch, roll);
	}
	
	/*
		Строит матрицу поворота на основе углов Эйлера
	*/
	
	static Matrix4 EulerToMatrix4(float pitch, float yaw, float roll) {
		float cy = cos(yaw);
		float sy = sin(yaw);
		float cp = cos(pitch);
		float sp = sin(pitch);
		float cr = cos(roll);
		float sr = sin(roll);
		return Matrix4(
			cy * cr + sy * sp * sr,
			sr * cp,
			- sy * cr + cy * sp * sr,
			0.0f,
			- cy * sr + sy * sp * cr,
			cr * cp,
			sr * sy + cy * sp * cr,
			0.0f,
			sy * cp,
			- sp,
			cy * cp,
			0.0f,
			0.0f,
			0.0f,
			0.0f,
			1.0f
		);
	}
	
	/*
		Строит матрицу поворота на основе углов Эйлера
	*/
	
	static Matrix4 EulerToMatrix4(const Vector3& eulerAngles) {
		float cy = cos(eulerAngles.yaw);
		float sy = sin(eulerAngles.yaw);
		float cp = cos(eulerAngles.pitch);
		float sp = sin(eulerAngles.pitch);
		float cr = cos(eulerAngles.roll);
		float sr = sin(eulerAngles.roll);
		return Matrix4(
			cy * cr + sy * sp * sr,
			sr * cp,
			- sy * cr + cy * sp * sr,
			0.0f,
			- cy * sr + sy * sp * cr,
			cr * cp,
			sr * sy + cy * sp * cr,
			0.0f,
			sy * cp,
			- sp,
			cy * cp,
			0.0f,
			0.0f,
			0.0f,
			0.0f,
			1.0f
		);
	}

	/*
		Масштабирование
	*/
		
	void scale(const Vector3& v) {
		data[ 0] *= v.x;
		data[ 1] *= v.x;
		data[ 2] *= v.x;
		data[ 3] *= v.x;
		data[ 4] *= v.y;
		data[ 5] *= v.y;
		data[ 6] *= v.y;
		data[ 7] *= v.y;
		data[ 8] *= v.z;
		data[ 9] *= v.z;
		data[10] *= v.z;
		data[11] *= v.z;
	}
	
	/*
		Масштабирование
	*/
		
	void scale(float x, float y, float z) {
		data[ 0] *= x;
		data[ 1] *= x;
		data[ 2] *= x;
		data[ 3] *= x;
		data[ 4] *= y;
		data[ 5] *= y;
		data[ 6] *= y;
		data[ 7] *= y;
		data[ 8] *= z;
		data[ 9] *= z;
		data[10] *= z;
		data[11] *= z;
	}

	/*
		Масштабирование
	*/
	
	static Matrix4 scale(const Matrix4& m, const Vector3& v) {
		return Matrix4(
			m.data[ 0] * v.x,
			m.data[ 1] * v.x,
			m.data[ 2] * v.x,
			m.data[ 3] * v.x,
			m.data[ 4] * v.y,
			m.data[ 5] * v.y,
			m.data[ 6] * v.y,
			m.data[ 7] * v.y,
			m.data[ 8] * v.z,
			m.data[ 9] * v.z,
			m.data[10] * v.z,
			m.data[11] * v.z,
			m.data[12],
			m.data[13],
			m.data[14],
			m.data[15]
		);
	}
	
	/*
		Масштабирование
	*/
	
	static Matrix4 scale(const Matrix4& m, float x, float y, float z) {
		return Matrix4(
			m.data[ 0] * x,
			m.data[ 1] * x,
			m.data[ 2] * x,
			m.data[ 3] * x,
			m.data[ 4] * y,
			m.data[ 5] * y,
			m.data[ 6] * y,
			m.data[ 7] * y,
			m.data[ 8] * z,
			m.data[ 9] * z,
			m.data[10] * z,
			m.data[11] * z,
			m.data[12],
			m.data[13],
			m.data[14],
			m.data[15]
		);
	}
	
	/*
		Перенос
	*/
	
	void translate(const Vector3& v) {
		data[12] += data[ 0] * v.x + data[ 4] * v.y + data[ 8] * v.z;
		data[13] += data[ 1] * v.x + data[ 5] * v.y + data[ 9] * v.z;
		data[14] += data[ 2] * v.x + data[ 6] * v.y + data[10] * v.z;
		data[15] += data[ 3] * v.x + data[ 7] * v.y + data[11] * v.z;
	}
	
	/*
		Перенос
	*/
	
	void translate(float x, float y, float z) {
		data[12] += data[ 0] * x + data[ 4] * y + data[ 8] * z;
		data[13] += data[ 1] * x + data[ 5] * y + data[ 9] * z;
		data[14] += data[ 2] * x + data[ 6] * y + data[10] * z;
		data[15] += data[ 3] * x + data[ 7] * y + data[11] * z;
	}

	/*
		Перенос
	*/

	static Matrix4 translate(const Matrix4& m, const Vector3& v) {
		return Matrix4 (
			m.data[ 0],
			m.data[ 1],
			m.data[ 2],
			m.data[ 3],

			m.data[ 4],
			m.data[ 5],
			m.data[ 6],
			m.data[ 7],

			m.data[ 8],
			m.data[ 9],
			m.data[10],
			m.data[11],
			
			m.data[ 0] * v.x + m.data[ 4] * v.y + m.data[ 8] * v.z + m.data[12],
			m.data[ 1] * v.x + m.data[ 5] * v.y + m.data[ 9] * v.z + m.data[13],
			m.data[ 2] * v.x + m.data[ 6] * v.y + m.data[10] * v.z + m.data[14],
			m.data[ 3] * v.x + m.data[ 7] * v.y + m.data[11] * v.z + m.data[15]
		);
	}
	
	/*
		Перенос
	*/

	static Matrix4 translate(const Matrix4& m, float x, float y, float z) {
		return Matrix4 (
			m.data[ 0],
			m.data[ 1],
			m.data[ 2],
			m.data[ 3],

			m.data[ 4],
			m.data[ 5],
			m.data[ 6],
			m.data[ 7],

			m.data[ 8],
			m.data[ 9],
			m.data[10],
			m.data[11],
			
			m.data[ 0] * x + m.data[ 4] * y + m.data[ 8] * z + m.data[12],
			m.data[ 1] * x + m.data[ 5] * y + m.data[ 9] * z + m.data[13],
			m.data[ 2] * x + m.data[ 6] * y + m.data[10] * z + m.data[14],
			m.data[ 3] * x + m.data[ 7] * y + m.data[11] * z + m.data[15]
		);
	}

	/*
		Возвращает матрицу орфографической проекции
	*/
	
	static Matrix4 ortho(float left, float right, float bottom, float top, float near, float far) {
		float rl = right - left;
		float tb = top - bottom;
		float fn = far - near;
		return Matrix4(
			2.0f / rl,
			0.0f,
			0.0f,
			0.0f,
			0.0f,
			2.0f / tb,
			0.0f,
			0.0f,
			0.0f,
			0.0f,
			-2.0f / fn,
			0.0f,
			-(right + left) / rl,
			-(top + bottom) / tb,
			-(far + near) / fn,
			1.0f
		);
	}
	
	/*
		Возвращает перспективную матрицу 
	*/
	
	static Matrix4 frustum(float left, float right, float bottom, float top, float near, float far) {
		float nn = near + near;
		float rl = right - left;
		float tb = top - bottom;
		float fn = far - near;
		return Matrix4 (
			nn / rl,
			0.0f,
			0.0f,
			0.0f,
			0.0f,
			nn / tb,
			0.0f,
			0.0f,
			(right + left) / rl,
			(top + bottom) / tb,
			-(far + near) / fn,
			-1.0f,
			0.0f,
			0.0f,
			-(nn * far) / fn,
			0.0f
		);
	}
	
	/*
		Возвращает перспективную матрицу на основе угла обзора fovy (радианы) 
		и соотношения ширины к высоте
	*/
	
	static Matrix4 perspective(float fovy, float aspect, float near, float far) {
		float f = 1.0f / tan(fovy / 2.0f);
		float nf = near - far;
		return Matrix4 (
			f / aspect,
			0.0f,
			0.0f,
			0.0f,
			0.0f,
			f,
			0.0f,
			0.0f,
			0.0f,
			0.0f,
			(far + near) / nf,
			-1.0f,
			0.0f,
			0.0f,
			(2.0f * far * near) / nf,
			0.0f
		);
	}
	
	/*
		Матрица камеры (взгляда)
	*/
	
	static Matrix4 lookAt(const Vector3& eye, const Vector3& center, const Vector3& up) {
		Matrix4 result;
		result.data[ 2] = center.x - eye.x;
		result.data[ 6] = center.y - eye.y;
		result.data[10] = center.z - eye.z;
		float invLength = 1.0f / sqrt(result.data[2] * result.data[2] + result.data[6] * result.data[6] + result.data[10] * result.data[10]);
		result.data[ 2] *= invLength;
		result.data[ 6] *= invLength;
		result.data[10] *= invLength;
		result.data[ 0] = result.data[6] * up.z - result.data[10] * up.y;
		result.data[ 4] = result.data[10] * up.x - result.data[2] * up.z;
		result.data[ 8] = result.data[2] * up.y - result.data[6] * up.x;
		invLength = 1.0f / sqrt(result.data[0] * result.data[0] + result.data[4] * result.data[4] + result.data[8] * result.data[8]);
		result.data[ 0] *= invLength;
		result.data[ 4] *= invLength;
		result.data[ 8] *= invLength;
		result.data[ 1] = result.data[4] * result.data[10] - result.data[8] * result.data[6];
		result.data[ 5] = result.data[8] * result.data[2] - result.data[0] * result.data[10];
		result.data[ 9] = result.data[0] * result.data[6] - result.data[4] * result.data[2];
		result.data[12] = - (result.data[0] * eye.x + result.data[4] * eye.y + result.data[8] * eye.z);
		result.data[13] = - (result.data[1] * eye.x + result.data[5] * eye.y + result.data[9] * eye.z);
		result.data[14] =   (result.data[2] * eye.x + result.data[6] * eye.y + result.data[10] * eye.z);
		result.data[ 3] = 0.0f;
		result.data[ 7] = 0.0f;
		result.data[11] = 0.0f;
		result.data[15] = 1.0f;
		result.data[ 2] = - result.data[ 2];
		result.data[ 6] = - result.data[ 6];
		result.data[10] = - result.data[10];
		return result;
	}

	/*
		Матрица камеры (взгляда)
	*/
	
	static Matrix4 lookAt(float centerX, float centerY, float centerZ, float eyeX, float eyeY, float eyeZ, float upX, float upY, float upZ) {
		Matrix4 result;
		result.data[ 2] = centerX - eyeX;
		result.data[ 6] = centerY - eyeY;
		result.data[10] = centerZ - eyeZ;
		float invLength = 1.0f / sqrt(result.data[2] * result.data[2] + result.data[6] * result.data[6] + result.data[10] * result.data[10]);
		result.data[ 2] *= invLength;
		result.data[ 6] *= invLength;
		result.data[10] *= invLength;
		result.data[ 0] = result.data[6] * upZ - result.data[10] * upY;
		result.data[ 4] = result.data[10] * upX - result.data[2] * upZ;
		result.data[ 8] = result.data[2] * upY - result.data[6] * upX;
		invLength = 1.0f / sqrt(result.data[0] * result.data[0] + result.data[4] * result.data[4] + result.data[8] * result.data[8]);
		result.data[ 0] *= invLength;
		result.data[ 4] *= invLength;
		result.data[ 8] *= invLength;
		result.data[ 1] = result.data[4] * result.data[10] - result.data[8] * result.data[6];
		result.data[ 5] = result.data[8] * result.data[2] - result.data[0] * result.data[10];
		result.data[ 9] = result.data[0] * result.data[6] - result.data[4] * result.data[2];
		result.data[12] = - (result.data[0] * eyeX + result.data[4] * eyeY + result.data[8] * eyeZ);
		result.data[13] = - (result.data[1] * eyeX + result.data[5] * eyeY + result.data[9] * eyeZ);
		result.data[14] =   (result.data[2] * eyeX + result.data[6] * eyeY + result.data[10] * eyeZ);
		result.data[ 3] = 0.0f;
		result.data[ 7] = 0.0f;
		result.data[11] = 0.0f;
		result.data[15] = 1.0f;
		result.data[ 2] = - result.data[ 2];
		result.data[ 6] = - result.data[ 6];
		result.data[10] = - result.data[10];
		return result;
	}

	/*
		Установит матрицу в единичное значение
	*/

	void loadIdentity() {
		*(Float16*)data = *(Float16*)idetityMatrixData;
	}
	
	/*
		Установит матрицу в единичное значение
	*/
	
	static void loadIdentity(const Matrix4& m) {
		*(Float16*)m.data = *(Float16*)idetityMatrixData;
	}
	
public:

	Matrix4(float s) {
		data[ 0] = s;
		data[ 1] = 0.0f;
		data[ 2] = 0.0f;
		data[ 3] = 0.0f;
		data[ 4] = 0.0f;
		data[ 5] = s;
		data[ 6] = 0.0f;
		data[ 7] = 0.0f;
		data[ 8] = 0.0f;
		data[ 9] = 0.0f;
		data[10] = s;
		data[11] = 0.0f;
		data[12] = 0.0f;
		data[13] = 0.0f;
		data[14] = 0.0f;
		data[15] = s;
	}
	
	Matrix4(
		float m00, float m01, float m02, float m03,
		float m04, float m05, float m06, float m07,
		float m08, float m09, float m10, float m11,
		float m12, float m13, float m14, float m15
	) {
		data[ 0] = m00;
		data[ 1] = m01;
		data[ 2] = m02;
		data[ 3] = m03;
		data[ 4] = m04;
		data[ 5] = m05;
		data[ 6] = m06;
		data[ 7] = m07;
		data[ 8] = m08;
		data[ 9] = m09;
		data[10] = m10;
		data[11] = m11;
		data[12] = m12;
		data[13] = m13;
		data[14] = m14;
		data[15] = m15;
	}

	Matrix4() {
	}
	
public:

	static const Matrix4 identityMatrix;
	
};

const Matrix4 Matrix4::identityMatrix = Matrix4(1.0f);

#endif